package org.spp.voronoi;

import org.spp.utilities.SetUtils;
import org.spp.utilities.QVoronoi;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * VoronoiDiagram.java
 * @author Ilio Catallo, Eleonora Ciceri
 * @version 1.0
 * 
 * Description: the class implements the voronoi diagram both in tridimensional
 *              and bidimensional vector spaces.
 * 
 * Date: Jul 10, 2011
 */
public class VoronoiDiagram {
    
    // Attributes
    private List<Centroid> centroids;
    private BoundedRegion boundedRegion;
    private List<Cell> cells;
    private QVoronoi qVoronoi;
    
    /**
     * Constructor of the class VoronoiDiagram
     * @param centroids 
     */
    public VoronoiDiagram(List<Centroid> centroids) {
        this.centroids = centroids;
        
        if (centroids.get(0).getCoordinates().length == 3)
            this.boundedRegion = new BoundedRegion("3D");
        else
            this.boundedRegion = new BoundedRegion("2D");
        this.qVoronoi = new QVoronoi("/usr/local/bin/qvoronoi");
        
        findAdjCentroids();
        generateCells();
    }
    
    /**
     * Method that returns the bounded region's facets
     * @return boundedRegionFacets
     */ 
    public List<Facet> getBoundedRegionFacets() {
        return boundedRegion.getFacets();
    }
    
    /**
     * Method that returns all the vertexes in the voronoi diagram
     * @return vertexes
     */
    public List<Vertex> getVertexes(){
        // Variables
        List<Vertex> vertexes = new ArrayList<Vertex>();
        
        for (int i = 0; i < cells.size(); i++)
            vertexes.addAll(cells.get(i).getVertexes());
        
        return vertexes;
    }
    
    /**
     * Method that identifies the adjacency between the centroids
     */ 
    private void findAdjCentroids() {
        // Variables
        int numCentroids = 0;
        
        for (int i = 0; i < centroids.size(); i++)
            centroids.get(i).removeAdjCentroids(); 
        
        if (centroids.get(0).getCoordinates().length == 3)
            numCentroids = 3;
        else
            numCentroids = 2;
        
        // If the centroids are less or equal to numCentroids
        if (centroids.size() <= numCentroids){
            
            for (int i = 0; i < centroids.size(); i++)
                for (int j = 0; j < centroids.size(); j++)
                    
                    if (i != j)    
                        centroids.get(i).addAdjCentroid(centroids.get(j));
            
        }  
        else {        
           // External call to qvoronoi
           HashMap facetsToVertexes = qVoronoi.run(centroids);

           // Find the adjacent centroids starting from the facetsToVertex matrix
           for (int i = 0; i < centroids.size(); i++){
               for (int j = 0; j < centroids.size(); j++){

                   if (i == j)
                       continue;

                   // Get the common vertexes between the two centroids
                   ArrayList<Integer> vertexesCentroid1 = (ArrayList<Integer>) facetsToVertexes.get(centroids.get(i));
                   ArrayList<Integer> vertexesCentroid2 = (ArrayList<Integer>) facetsToVertexes.get(centroids.get(j));
                   ArrayList<Integer> commonVertexes = (ArrayList<Integer>) SetUtils.findIntersection(vertexesCentroid1, vertexesCentroid2);

                   // The two centroids are adjacent if there's at least one common vertex
                   if (!commonVertexes.isEmpty())
                       centroids.get(i).addAdjCentroid(centroids.get(j));

               } 
           }
        }
        
    }
    
    /**
     * Method for the generation of the cells
     */
    private void generateCells() {
        // Variables
        cells = new ArrayList<Cell>();
        
        if (centroids.get(0).getCoordinates().length == 2)
            for (int i = 0; i < centroids.size(); i++) 
                cells.add(new Cell2D(centroids.get(i), i, this));
        else   
            for (int i = 0; i < centroids.size(); i++) 
                cells.add(new Cell3D(centroids.get(i), i, this));
        
        System.out.println("Empty cells created. Number of cells: " + cells.size());
        
        for (int i = 0; i < cells.size(); i++) 
            cells.get(i).generate();
        
        for (int i = 0; i < cells.size(); i++)
            cells.get(i).computeEdges();
        
    }
    
}